<?php
declare(strict_types=1);

namespace app\shop\logic;

use app\shop\exception\FailException;
use app\shop\library\calculate\Calculate;
use app\shop\library\ShopAuth;
use app\shop\library\ShopHelper;
use app\shop\model\goods\GoodsClassifyModel;
use app\shop\model\goods\GoodsModel;
use app\shop\model\goods\GoodsServiceModel;
use app\shop\model\order\OrderEvaluateModel;
use app\shop\model\user\UserFavoriteModel;
use app\shop\service\CouponService;
use ba\Tree;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\MiniApp\Application;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\helper\Arr;
use think\Paginator;
use Throwable;

/**
 * @property GoodsModel $model
 */
class GoodsLogic extends BaseLogic
{
    public function __construct()
    {
        $this->model = new GoodsModel();
    }

    /**
     * 订单列表
     * @param array $params
     * @return array
     */
    public function list(array $params): array
    {
        $query = GoodsModel::getQuery();

        $query->where([
            ['status', '=', 1]
        ]);

        if (Arr::get($params, 'classify_id')) {
            $query->whereFindInSet('classify_id', $params['classify_id']);
        }
        if (Arr::get($params, 'coupon_id')) {
            $userId   = ShopAuth::instance()->id ?? 0;
            $goodsIds = (new CouponService())->getCouponRangeGoodsIds($params['coupon_id'], $userId);
            if (!empty($goodsIds)) {
                $query->whereIn('id', $goodsIds);
            }
        }

        $this->buildLikeQuery($query, $params, [
            'name' => 'name',
        ]);

        $query->field([
            'id', 'name', 'images', 'sales_init', 'sales_real', '(sales_init + sales_real) as total_sales',
            'min_price', 'max_origin_price', 'total_stock'
        ]);

        $query->hidden(['sales_init', 'sales_real']);

        if (Arr::get($params, 'sort_field') === 'all') {
            $query->order('id', 'desc');
        }
        if (Arr::get($params, 'sort_field') === 'sales') {
            $query->order('total_sales', 'desc');
        }
        if (Arr::get($params, 'sort_field') === 'price') {
            $query->order('min_price', $params['sort_by']);
        }

        $query->order(['weigh' => 'desc', 'id' => 'desc']);

        return GoodsModel::paginateListQuery($query, true);
    }

    /**
     * 订单详情
     * @param array $params
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws FailException
     * @throws ModelNotFoundException
     */
    public function detail(array $params): array
    {
        $goodsId = $params['id'] ?? 0;

        $query = GoodsModel::where([
            ['id', '=', $goodsId],
            ['status', '=', 1]
        ]);

        $result = $query->withoutField(['create_time', 'update_time'])
            ->with([
                'evaluate' => function ($query) {
                    $query->where('status', 1)
                        ->field(['goods_id', 'score', 'goods_spec', 'content', 'user_name', 'user_avatar', 'images'])
                        ->order(['score' => 'desc', 'create_time' => 'desc'])
                        ->limit(2);
                },
            ])
            ->append(['sku_list'])
            ->findOrEmpty();

        if ($result->isEmpty()) {
            throw new FailException('商品已下架');
        }

        if ($result['service_id']) {
            $result->goods_service = GoodsServiceModel::where(['id' => explode(',', $result['service_id'])])
                ->select()
                ->toArray();
        } else {
            $result->goods_service = [];
        }

        $orderEvaluateModel     = new OrderEvaluateModel();
        $result->evaluate_count = $orderEvaluateModel
            ->where('goods_id', $goodsId)
            ->where('status', 1)
            ->count();
        if ($result->evaluate_count <= 0) {
            $result->positive_rate = 0;
        } else {
            $positiveEvaluate = $orderEvaluateModel->where([
                ['status', '=', 1],
                ['goods_id', '=', $goodsId],
                ['score', '>=', 4]
            ])->count();

            $evaluateRate          = Calculate::newDecimal($positiveEvaluate)->div($result->evaluate_count)->getValue();
            $result->positive_rate = Calculate::newDecimal($evaluateRate)->mul(100)->getValue();
        }

        GoodsModel::where(['id' => $result->id])->inc('visit')->save();

        if ($params['user_id']) {
            $result['favorite'] = UserFavoriteModel::where(['user_id' => $params['user_id'], 'goods_id' => $result['id']])->count() > 0;
        } else {
            $result['favorite'] = false;
        }

        return $result->toArray();
    }

    /**
     * 一级分类列表
     * @return array
     */
    public function topClassify(): array
    {
        $query = GoodsClassifyModel::getQuery();

        $query->where([
            ['status', '=', 1],
            ['pid', '=', 0]
        ]);

        $query->order(['weigh' => 'desc']);

        $query->field(['id', 'name', 'icon']);

        return GoodsClassifyModel::listQuery($query);
    }

    /**
     * 获取所有分类
     * @param array $params
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getAllClassify(array $params): array
    {
        $classify = GoodsClassifyModel::where(['status' => 1])
            ->field(['id', 'pid', 'name', 'icon', 'weigh'])
            ->order('weigh', 'desc')
            ->select()
            ->toArray();

        $classifyData = Tree::instance()->assembleChild($classify);

        foreach ($classifyData as $key => &$data) {
            if ($data['pid'] != 0) {
                unset($classifyData[$key]);
            }
            foreach ($data['children'] as &$child) {
                $classify_id          = $child['pid'] . '-' . $child['id'];
                $child['classify_id'] = $classify_id;
            }
        }

        return $classifyData;
    }

    /**
     * 通过分类获取商品列表
     * @param array $params
     * @return array|Paginator
     */
    public function getGoodsListByClassify(array $params): Paginator|array
    {
        $query = GoodsModel::getQuery();

        $query->where('status', 1)->whereFindInSet('classify_id', $params['classify_id']);

        $query->field(['id', 'name', 'images', 'max_origin_price', 'min_price']);

        return GoodsModel::paginateListQuery($query);
    }

    /**
     * 商品服务与承诺列表
     * @param array $params
     * @return Collection|array
     */
    public function getServiceList(array $params): Collection|array
    {
        $query = GoodsServiceModel::getQuery();

        $serviceIds = explode(',', $params['service_ids']);

        $query->where(['id' => $serviceIds]);

        $query->field(['id', 'name', 'content']);

        return GoodsServiceModel::listQuery($query);
    }

    /**
     * @throws FailException
     */
    public function shareInfo(array $params): array
    {
        $goodsId = $params['id'];
        $userId  = $params['user_id'];

        $config = ShopHelper::get_setting_group('miniapp_config');

        try {
            $app = new Application([
                'app_id' => $config['miniapp_id'],
                'secret' => $config['miniapp_secret'],
            ]);
        } catch (InvalidArgumentException $e) {
            throw new FailException('小程序配置错误', null, $e);
        }

        $page = "pagesGoods/pages/goods/goods";

        $queryParams = [
            'scene' => "id=$goodsId&sharer=$userId",
            'page'  => $page,
        ];

        $saveDirPath        = '/storage/qrcode/goods_share';
        $qrcodeRelativePath = "$saveDirPath/id_{$goodsId}_sharer_$userId.png";
        $codeFilePath       = public_path() . $qrcodeRelativePath;

        try {
            if (!is_dir(public_path($saveDirPath))) {
                mkdir(public_path($saveDirPath), 0755, true);
            }
            if (!file_exists($codeFilePath)) {
                $app->getClient()->postJson('/wxa/getwxacodeunlimit', $queryParams)->saveAs($codeFilePath);
            }
        } catch (Throwable $e) {
            throw new FailException('二维码生成失败', null, $e, $queryParams, true);
        }

        $qrcodeUrl = request()->host() . $qrcodeRelativePath;
        $scheme    = parse_url('https://' . $qrcodeUrl) ? 'https://' : 'http://';

        return [
            'qrcode_url' => $scheme . $qrcodeUrl,
        ];
    }
}